package com.baymax.test.scene.demo.maverick

import android.R.attr.fragment
import android.os.Bundle
import android.os.Parcelable
import androidx.activity.ComponentActivity
import androidx.lifecycle.LifecycleOwner
import com.airbnb.mvrx.ActivityViewModelContext
import com.airbnb.mvrx.DeliveryMode
import com.airbnb.mvrx.InternalMavericksApi
import com.airbnb.mvrx.Mavericks
import com.airbnb.mvrx.MavericksDelegateProvider
import com.airbnb.mvrx.MavericksState
import com.airbnb.mvrx.MavericksStateFactory
import com.airbnb.mvrx.MavericksView
import com.airbnb.mvrx.MavericksViewModel
import com.airbnb.mvrx.MavericksViewModelProvider
import com.airbnb.mvrx.RealMavericksStateFactory
import com.airbnb.mvrx.RedeliverOnStart
import com.airbnb.mvrx.ViewModelContext
import com.airbnb.mvrx.lifecycleAwareLazy
import com.bytedance.scene.Scene
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KClass
import kotlin.reflect.KProperty


/**
 * For internal use only. Public for inline.
 *
 * Looks for [Mavericks.KEY_ARG] on the arguments of the fragments.
 */
@Suppress("FunctionName", "DEPRECATION")
@InternalMavericksApi
fun <T : Scene> T._fragmentArgsProvider(): Any? = arguments?.get(Mavericks.KEY_ARG)

/**
 * Gets or creates a ViewModel scoped to this Fragment. You will get the same instance every time for this Fragment, even
 * through rotation, or other configuration changes.
 *
 * If the ViewModel has additional dependencies, implement [MavericksViewModelFactory] in its companion object.
 * You will be given the initial state as well as a FragmentActivity with which you can access other dependencies to
 * pass to the ViewModel's constructor.
 *
 * Mavericks will also handle persistence across process restarts. Refer to [PersistState] for more info.
 *
 * Use [keyFactory] if you have multiple ViewModels of the same class in the same scope.
 */
@OptIn(InternalMavericksApi::class)
inline fun <T, reified VM : MavericksViewModel<S>, reified S : MavericksState> T.sceneViewModel(
    viewModelClass: KClass<VM> = VM::class,
    crossinline keyFactory: () -> String =  viewModelClass.java::getName,
): MavericksDelegateProvider<T, VM> where T : Scene, T : MavericksView =
    object : MavericksDelegateProvider<T, VM>() {
        override operator fun provideDelegate(
            thisRef: T,
            property: KProperty<*>
        ): Lazy<VM> {
            return lifecycleAwareLazy(owner = thisRef) {
                val apply = MavericksViewModelProvider.get(
                    viewModelClass = viewModelClass.java,
                    stateClass = S::class.java,
                    key =  keyFactory(),
                    viewModelContext = ActivityViewModelContext(
                        owner = thisRef,
                        activity = thisRef.requireActivity() as ComponentActivity,
                        args = _fragmentArgsProvider(),
                    ),
//                    initialStateFactory =  initialStateFactory()
                ).apply {
                    stateFlow.collectLatest(RedeliverOnStart) { thisRef.postInvalidate() }
                }
                apply
            }
        }
    }


fun <V : Any> args() = object : ReadOnlyProperty<Scene, V> {
    var value: V? = null

    @Suppress("DEPRECATION")
    override fun getValue(thisRef: Scene, property: KProperty<*>): V {
        if (value == null) {
            val args = thisRef.arguments
                ?: throw IllegalArgumentException("There are no fragment arguments!")
            // Sometimes the class loader of the arguments is java.lang.BootClassLoader and thus the args bundle
            // cannot be loaded. Ensure that it has the same classloader as the Fragment.
            args.classLoader = thisRef::class.java.classLoader
            val argUntyped = args.get(Mavericks.KEY_ARG)
            argUntyped
                ?: throw IllegalArgumentException("Mavericks arguments not found at key _root_ide_package_.com.airbnb.mvrx.Mavericks.KEY_ARG!")
            @Suppress("UNCHECKED_CAST")
            value = argUntyped as V
        }
        return value ?: throw IllegalArgumentException("")
    }
}

/**
 * Fragment argument delegate that makes it possible to set fragment args without
 * creating a key for each one.
 *
 * To create nullable arguments, define a property in your fragment like:
 *     `private val listingId: MyArgs? by argsOrNull()`
 *
 * Each fragment can only have a single argument with the key [Mavericks.KEY_ARG]
 */
fun <V> argsOrNull() = object : ReadOnlyProperty<Scene, V?> {
    var value: V? = null
    var read: Boolean = false

    @Suppress("DEPRECATION")
    override fun getValue(thisRef: Scene, property: KProperty<*>): V? {
        if (!read) {
            val args = thisRef.arguments
            // Sometimes the class loader of the arguments is java.lang.BootClassLoader and thus the args bundle
            // cannot be loaded. Ensure that it has the same classloader as the Fragment.
            args?.classLoader = thisRef::class.java.classLoader
            val argUntyped = args?.get(Mavericks.KEY_ARG)
            @Suppress("UNCHECKED_CAST")
            value = argUntyped as? V
            read = true
        }
        return value
    }
}